#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/cdev.h>
#include <linux/delay.h>

#include <linux/pci_ids.h>
#include "z5registers.h"

// use license "GPL" to avoid "tainted" kernels
MODULE_LICENSE("Proprietary");
MODULE_AUTHOR("Mickey Mouse");
int save_reg[19];
int major = 127;
int minor = 500;
struct cdev whatever;
struct pci_dev *pci_dev1; 

struct zach5_sdev {
unsigned int p_control_base;
unsigned int *k_control_base;
unsigned int *u_control_base;
unsigned int pci_irq;
}zach5;


struct pci_device_id zach5_dev_ids[] = 
{
{PCI_DEVICE(PCI_VENDOR_ID_ZJONES,PCI_DEVICE_ID_ZACH5)},
{0}
};

int zach5_remove()
{
printk(KERN_ALERT "in zach5 remove\n");
return(0);

}

  

int zach5_probe(struct pci_dev *pci_dev,const struct pci_device_id *pci_id)
{
	int z;
 
	zach5.p_control_base = pci_resource_start(pci_dev,0);
 	 zach5.pci_irq = pci_dev->irq;
  	 z=pci_enable_device(pci_dev);
         pci_set_master(pci_dev);
	 printk(KERN_ALERT "In Zach5 Probe\n");
 
	 return(0);
}

int zach5_open(struct inode *inode, struct file *fp) 
{
        zach5.k_control_base = ioremap(zach5.p_control_base,1024);
	printk(KERN_ALERT "Opened zach5\n");
	return 0;
}

int zach5_release(struct inode *inode, struct file *fp) {
	printk(KERN_ALERT "Released zach5\n");
	return 0;
}


struct pci_driver zach5_pci_drv = 
{
 .name = "Testdevice",
 .id_table = zach5_dev_ids,
 .probe = zach5_probe,	
 .remove = zach5_remove
};



int zach5_mmap(struct file *fp,struct vm_area_struct *vma)
{
   printk(KERN_ALERT "Inside zach5 mmap");
   io_remap_pfn_range(vma,vma->vm_start,(zach5.p_control_base)>>PAGE_SHIFT,vma->vm_end - vma->vm_start,vma->vm_page_prot);
   zach5.u_control_base = (unsigned int*)vma -> vm_start;
  return 0;
}

unsigned int K_READ_REG(unsigned int reg)
{
 unsigned int value;
 udelay(1);
 rmb();
 value =  *(zach5.k_control_base+(reg>>2));
 udelay(1);
 return (value);
}
unsigned int K_WRITE_REG(unsigned int reg, unsigned int value)
{
  udelay(1);
  *(zach5.k_control_base+(reg>>2)) = value;
  udelay(1);
  return (0);
}

 


//registers to save
void saveRegisters()
{
 printk(KERN_ALERT "Inside save regs\n");
 save_reg[0] = K_READ_REG(Aperture1);
 save_reg[1]  = K_READ_REG(Aperture2);
 save_reg[2] = K_READ_REG(FrameBufferWriteMask); 
 save_reg[3] = K_READ_REG(DisconDFIFO); 
 save_reg[4] = K_READ_REG(DisconFIFO); 
 save_reg[5] = K_READ_REG(BootAddress); 
 save_reg[6] = K_READ_REG(MemoryCfg); 
 save_reg[7] = K_READ_REG(CtrlGraphics); 
 save_reg[8] = K_READ_REG(HgEnd); 
 save_reg[9] = K_READ_REG(ScreenBase); 
 save_reg[10] = K_READ_REG(HTotal); 
 save_reg[11] = K_READ_REG(HbEnd);
 save_reg[12] = K_READ_REG(HsStart);
 save_reg[13] = K_READ_REG(HsEnd);
 save_reg[14] = K_READ_REG(VTotal);
 save_reg[15] = K_READ_REG(VbEnd);
 save_reg[16] = K_READ_REG(VsStart);
 save_reg[17] = K_READ_REG(VsEnd);
 save_reg[18] = K_READ_REG(CfgGraphics);
}





void loadResolutionDependentRegisters()
{
 printk(KERN_ALERT "inside load resol Dependent regs");
 K_WRITE_REG(Aperture1,0x0);
 K_WRITE_REG(Aperture2,0x0);
 K_WRITE_REG(HTotal,0x041F0000); 
 K_WRITE_REG(HbEnd,0x100);
 K_WRITE_REG(HsStart,0x1000);
 K_WRITE_REG(HsEnd,0x60000);
 K_WRITE_REG(HgEnd,0x1000); 
 K_WRITE_REG(VTotal,0x2700000);
 K_WRITE_REG(VsStart,0x0);
 K_WRITE_REG(VsEnd,0x3000);
 K_WRITE_REG(VbEnd,0x19);
}

void loadControlRegisters()
{
         printk(KERN_ALERT "Inside load control regs\n");
	 K_WRITE_REG(InterruptLine,0x0);
	 K_WRITE_REG(DisplayData,0x0);
	 K_WRITE_REG(FIFOCtrl,0x8010); 
	 K_WRITE_REG(LineCount,0x0); 
	 K_WRITE_REG(ScreenBaseRight,0x0); 
	 K_WRITE_REG(ScreenBase,0x0); 
	 K_WRITE_REG(FlagsInt,0x0); 
	 K_WRITE_REG(VClkCtl,0x40000); 
	 K_WRITE_REG(DMACtrl,0x0); 
	 K_WRITE_REG(DisconFIFO,0x10); 
	 K_WRITE_REG(MemoryCtrl,0x0); 
	 K_WRITE_REG(WriteMaskBypass,0xffffffff); 
	 K_WRITE_REG(BootAddress,0x18C000);
	 K_WRITE_REG(MemoryCfg,0xe6002021); 
	

}
void restoreRegisters()
{
 printk(KERN_ALERT "Restoring registers\n");
 K_WRITE_REG(Aperture1,0x0);
 K_WRITE_REG(Aperture2,0x0);
 K_WRITE_REG(WriteMaskBypass,0xffffffff); 
 K_WRITE_REG(DisconDFIFO,0x0); 
 K_WRITE_REG(DisconFIFO,0x0); 
 K_WRITE_REG(BootAddress,0x20); 
 K_WRITE_REG(MemoryCfg,0xe6002021); 
 K_WRITE_REG(CtrlGraphics,save_reg[7]); 
 K_WRITE_REG(HgEnd,0x1); 
 K_WRITE_REG(ScreenBase,0x7fa92);
 K_WRITE_REG(VClkCtl,0x80); 
 K_WRITE_REG(HTotal,0xd0); 
 K_WRITE_REG(HbEnd,0x1);
 K_WRITE_REG(HsStart,0x1ca);
 K_WRITE_REG(HsEnd,0x8);
 K_WRITE_REG(VTotal,0x300);
 K_WRITE_REG(VbEnd,0x30);
 K_WRITE_REG(VsStart,0x200);
 K_WRITE_REG(VsEnd, 0xa);
 K_WRITE_REG(VsEnd,save_reg[17]);
 K_WRITE_REG(CfgGraphics,save_reg[18]);
}

void drawSquare()
{
	printk(KERN_ALERT "Inside draw square");	
	K_WRITE_REG(R0, 4194304);
	K_WRITE_REG(G0, 4194304);
	K_WRITE_REG(B0, 0);
	
	K_WRITE_REG(dRdyDom, 4064);
	K_WRITE_REG(dGdyDom, 4064);
	K_WRITE_REG(dBdyDom, 0);
	
	K_WRITE_REG(RasterizerCfg, 0x40000000);
	K_WRITE_REG(XDom0, 655360);
	K_WRITE_REG(dXDom, 0);
	
	K_WRITE_REG(XSub0, 51773440);
	K_WRITE_REG(dXSub, 0);
	K_WRITE_REG(Y0, 655360);
	
	
	K_WRITE_REG(dY,65536);
 	K_WRITE_REG(Count,580<<18);

	K_WRITE_REG(Render,RENDER_TRAPEZOID_SPC);
	
}

void drawTriangle()
{
	printk(KERN_ALERT "Inside draw square");	
	K_WRITE_REG(R0, 6553600);
	K_WRITE_REG(G0, 0);
	K_WRITE_REG(B0, 0);
	
	K_WRITE_REG(dRdyDom, 5232);
	K_WRITE_REG(dGdyDom, 0);
	K_WRITE_REG(dBdyDom, 0);
	
	K_WRITE_REG(dRdx, 4128);
	K_WRITE_REG(dGdx, 0);
	K_WRITE_REG(dBdx, 0);
	
	K_WRITE_REG(ZStartU, 0);
	K_WRITE_REG(ZStartL, 0);
	K_WRITE_REG(dZdyDomU, 0);
	K_WRITE_REG(dZdyDomL, 0);
	K_WRITE_REG(dZdxU, 0);
	K_WRITE_REG(dZdxL, 0);
	
	K_WRITE_REG(XDom0, 3276800);
	K_WRITE_REG(dXDom, -147456);
	
	K_WRITE_REG(XSub0, 3276800);
	K_WRITE_REG(dXSub, 39321);
	K_WRITE_REG(Y0, 26214400);
	
	K_WRITE_REG(dY,65536);
 	K_WRITE_REG(Count,250<<18);
	
	K_WRITE_REG(XDom0, 32768000);
	K_WRITE_REG(dXDom, -147456);
	
	K_WRITE_REG(XSub0, 32768000);
	K_WRITE_REG(dXSub, -43690);
	K_WRITE_REG(Y0, 42598400);
	
	K_WRITE_REG(dY,65536);
 	K_WRITE_REG(Count,450<<18);

	K_WRITE_REG(XSub0, 13107200);
	K_WRITE_REG(RasterizerCfg, 0x0);

	K_WRITE_REG(Render,RENDER_TRAPEZOID_SPC);
}

int zach5_ioctl(struct file *fp, unsigned int cmd, unsigned int arg)
{
	
	printk(KERN_ALERT "IN ZACH5 IOCTL\n");
	switch(cmd)
	{
		case VMODE:
			if(arg == 1)
                       {
				printk(KERN_ALERT "In graphics ON");
				saveRegisters();
				loadResolutionDependentRegisters();
				loadControlRegisters();
			//	drawTriangle();
				drawSquare();
				udelay(5000);
				K_WRITE_REG(CfgGraphics,0x80020006);
				K_WRITE_REG(CtrlGraphics,0x82132000);
                        }
 			if(arg == 0)
			{
				printk(KERN_ALERT "In graphics OFF");
				restoreRegisters();
				K_WRITE_REG(CtrlGraphics,0x0);
				K_WRITE_REG(CfgGraphics,0x80020004);
				K_WRITE_REG(Reboot, 0x01);
			}
		break;

		/* case DMA_BIND:
		break;

		case DMA_START:
		break;
		*/ 

		default:
		break; 
		

	}
	    
	return(0); 
}


struct file_operations zach5_fops = {
	.open=		zach5_open,
	.release=	zach5_release,
	.owner=		THIS_MODULE,
	.mmap =         zach5_mmap, 
	.unlocked_ioctl = zach5_ioctl
	};


static int zach_init(void) {
	cdev_init(&whatever, &zach5_fops);
        zach5_fops.owner = THIS_MODULE;     
	cdev_add(&whatever, MKDEV(major,minor),1);
        printk(KERN_ALERT "Inside zach init");   
	int results = pci_register_driver(&zach5_pci_drv);
	return(0);
}

static int zach_exit(void) 
{
	pci_unregister_driver(&zach5_pci_drv);
	cdev_del(&whatever);
	printk(KERN_ALERT "Successfully removed device\n");
	return(0);
}

module_init(zach_init);
module_exit(zach_exit);
